home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 26
/
Cream of the Crop 26.iso
/
os2
/
octa209s.zip
/
octave-2.09
/
src
/
rand.cc
< prev
next >
Wrap
C/C++ Source or Header
|
1997-07-19
|
9KB
|
413 lines
/*
Copyright (C) 1996 John W. Eaton
This file is part of Octave.
Octave is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
Octave is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with Octave; see the file COPYING. If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* Modified by Klaus Gebhardt, 1997 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <ctime>
#include <string>
#include "f77-fcn.h"
#include "defun-dld.h"
#include "error.h"
#include "gripes.h"
#include "help.h"
#include "mappers.h"
#include "oct-obj.h"
#include "unwind-prot.h"
#include "utils.h"
// Possible distributions of random numbers. This was handled with an
// enum, but unwind_protecting that doesn't work so well.
#define uniform_dist 1
#define normal_dist 2
// Current distribution of random numbers.
static int current_distribution = uniform_dist;
// Has the seed been set yet?
static int initialized = 0;
extern "C"
{
int *F77_FCN (dgennor, DGENNOR) (const double&, const double&,
double&);
int *F77_FCN (dgenunf, DGENUNF) (const double&, const double&,
double&);
int *F77_FCN (setall, SETALL) (const int&, const int&);
int *F77_FCN (getsd, GETSD) (int&, int&);
int *F77_FCN (setsd, SETSD) (const int&, const int&);
int *F77_FCN (setcgn, SETCGN) (const int&);
}
static double
curr_rand_seed (void)
{
union d2i { double d; int i[2]; };
union d2i u;
F77_XFCN (getsd, GETSD, (u.i[0], u.i[1]));
return u.d;
}
static int
force_to_fit_range (int i, int lo, int hi)
{
assert (hi > lo && lo >= 0 && hi > lo);
i = i > 0 ? i : -i;
if (i < lo)
i = lo;
else if (i > hi)
i = i % hi;
return i;
}
static void
set_rand_seed (double val)
{
union d2i { double d; int i[2]; };
union d2i u;
u.d = val;
int i0 = force_to_fit_range (u.i[0], 1, 2147483563);
int i1 = force_to_fit_range (u.i[1], 1, 2147483399);
F77_XFCN (setsd, SETSD, (i0, i1));
}
static char *
curr_rand_dist (void)
{
if (current_distribution == uniform_dist)
return "uniform";
else if (current_distribution == normal_dist)
return "normal";
else
{
panic_impossible ();
return 0;
}
}
// Make the random number generator give us a different sequence every
// time we start octave unless we specifically set the seed. The
// technique used below will cycle monthly, but it it does seem to
// work ok to give fairly different seeds each time Octave starts.
static void
do_initialization (void)
{
time_t now;
struct tm *tm;
time (&now);
tm = localtime (&now);
int hour = tm->tm_hour + 1;
int minute = tm->tm_min + 1;
int second = tm->tm_sec + 1;
int s0 = tm->tm_mday * hour * minute * second;
int s1 = hour * minute * second;
s0 = force_to_fit_range (s0, 1, 2147483563);
s1 = force_to_fit_range (s1, 1, 2147483399);
F77_XFCN (setall, SETALL, (s0, s1));
initialized = 1;
}
static octave_value_list
do_rand (const octave_value_list& args, int nargin)
{
octave_value_list retval;
int n = 0;
int m = 0;
if (nargin == 0)
{
n = 1;
m = 1;
goto gen_matrix;
}
else if (nargin == 1)
{
octave_value tmp = args(0);
if (tmp.is_string ())
{
string s_arg = tmp.string_value ();
if (s_arg == "dist")
{
retval(0) = curr_rand_dist ();
}
else if (s_arg == "seed")
{
retval(0) = curr_rand_seed ();
}
else if (s_arg == "uniform")
{
current_distribution = uniform_dist;
F77_XFCN (setcgn, SETCGN, (uniform_dist));
}
else if (s_arg == "normal")
{
current_distribution = normal_dist;
F77_XFCN (setcgn, SETCGN, (normal_dist));
}
else
error ("rand: unrecognized string argument");
}
else if (tmp.is_scalar_type ())
{
double dval = tmp.double_value ();
if (xisnan (dval))
{
error ("rand: NaN is invalid a matrix dimension");
}
else
{
m = n = NINT (tmp.double_value ());
if (! error_state)
goto gen_matrix;
}
}
else if (tmp.is_range ())
{
Range r = tmp.range_value ();
n = 1;
m = r.nelem ();
goto gen_matrix;
}
else if (tmp.is_matrix_type ())
{
// XXX FIXME XXX -- this should probably use the function
// from data.cc.
Matrix a = args(0).matrix_value ();
if (error_state)
return retval;
n = a.rows ();
m = a.columns ();
if (n == 1 && m == 2)
{
n = NINT (a (0, 0));
m = NINT (a (0, 1));
}
else if (n == 2 && m == 1)
{
n = NINT (a (0, 0));
m = NINT (a (1, 0));
}
else
warning ("rand (A): use rand (size (A)) instead");
goto gen_matrix;
}
else
{
gripe_wrong_type_arg ("rand", tmp);
return retval;
}
}
else if (nargin == 2)
{
if (args(0).is_string ())
{
if (args(0).string_value () == "seed")
{
double d = args(1).double_value ();
if (! error_state)
set_rand_seed (d);
}
}
else
{
double dval = args(0).double_value ();
if (xisnan (dval))
{
error ("rand: NaN is invalid as a matrix dimension");
}
else
{
n = NINT (dval);
if (! error_state)
{
m = NINT (args(1).double_value ());
if (! error_state)
goto gen_matrix;
}
}
}
}
return retval;
gen_matrix:
if (n == 0 || m == 0)
{
Matrix m;
retval.resize (1, m);
}
else if (n > 0 && m > 0)
{
Matrix rand_mat (n, m);
for (int j = 0; j < m; j++)
for (int i = 0; i < n; i++)
{
double val;
switch (current_distribution)
{
case uniform_dist:
F77_XFCN (dgenunf, DGENUNF, (0.0, 1.0, val));
rand_mat (i, j) = val;
break;
case normal_dist:
F77_XFCN (dgennor, DGENNOR, (0.0, 1.0, val));
rand_mat (i, j) = val;
break;
default:
panic_impossible ();
break;
}
}
retval(0) = rand_mat;
}
else
error ("rand: invalid negative argument");
return retval;
}
DEFUN_DLD (rand, args, nargout,
"rand -- generate a random value from a uniform distribution\n\
\n\
rand (N) -- generate N x N matrix\n\
rand (size (A)) -- generate matrix the size of A\n\
rand (N, M) -- generate N x M matrix\n\
rand (SEED) -- get current seed\n\
rand (SEED, N) -- set seed\n\
\n\
See also: randn")
{
octave_value_list retval;
int nargin = args.length ();
if (nargin > 2 || nargout > 1)
print_usage ("rand");
else
{
if (! initialized)
do_initialization ();
retval = do_rand (args, nargin);
}
return retval;
}
static void
reset_rand_generator (void *)
{
F77_XFCN (setcgn, SETCGN, (current_distribution));
}
DEFUN_DLD (randn, args, nargout,
"randn -- generate a random value from a normal distribution\n\
\n\
randn (N) -- generate N x N matrix\n\
randn (size (A)) -- generate matrix the size of A\n\
randn (N, M) -- generate N x M matrix\n\
randn (SEED) -- get current seed\n\
randn (SEED, N) -- set seed\n\
\n\
See also: rand")
{
octave_value_list retval;
int nargin = args.length ();
if (nargin > 2 || nargout > 1)
print_usage ("randn");
else
{
if (! initialized)
do_initialization ();
begin_unwind_frame ("randn");
// This relies on the fact that elements are popped from the
// unwind stack in the reverse of the order they are pushed
// (i.e. current_distribution will be reset before calling
// reset_rand_generator()).
add_unwind_protect (reset_rand_generator, 0);
unwind_protect_int (current_distribution);
current_distribution = normal_dist;
F77_XFCN (setcgn, SETCGN, (normal_dist));
retval = do_rand (args, nargin);
run_unwind_frame ("randn");
}
return retval;
}
/*
;;; Local Variables: ***
;;; mode: C++ ***
;;; End: ***
*/